﻿//======================================================================
//
//	Cross-Platform XNA Menu System
//  http://cpxms.codeplex.com/
//
//  All code and content licensed under Microsoft Public License (Ms-PL)	
//
//======================================================================
//
//	File:		ActionManager.cs
//
//	Author:		MJP
//
//	Date:		04/12/09
//
//======================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using System.Reflection;
using System.Collections.ObjectModel;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

using CPXMenu;

using CPXMenuEditor.Forms;
using CPXMenuEditor.Properties;

namespace CPXMenuEditor
{
	/// <summary>
	/// Provides a single point-of-entry for all user-initiated actions
	/// that affect the project being edited.  Keeps track of these actions,
	/// and allows them to be undone/redone.  Also keeps track of selected
	/// items in the project.
	/// </summary>
    public class ActionManager
    {
        private MainFrm mainForm;
        private MenuProject project = null;
        private MenuProjectHost projectHost;
        private Stack<IEditAction> undoActions = new Stack<IEditAction>();
        private Stack<IEditAction> redoActions = new Stack<IEditAction>();
        private bool dirty = false;
        private List<MenuItem> selectedItems = new List<MenuItem>();
		private string projectFileName = string.Empty;

        private bool compoundingAction = false;
        private List<IEditAction> compoundActions = new List<IEditAction>();

		/// <summary>
		/// Gets or sets the current MenuProject
		/// </summary>
        public MenuProject MenuProject
        {
            get { return project; }
            set 
            {
                if (project != value)
                {
                    MenuProject oldProject = project;
                    project = value;
					projectHost.MenuProject = project;
                    OnProjectChanged(oldProject, project);
                }
            }
        }

        public MenuProjectHost ProjectHost
        {
            get { return projectHost; }
        }

		/// <summary>
		/// Gets or sets a value indicating whether the project is "dirty" (needs to be saved)
		/// </summary>
        public bool Dirty
        {
            get { return dirty; }
            set { dirty = value; }
        }

		/// <summary>
		/// Gets a value indicating whether an "undo" can be done
		/// </summary>
        public bool CanUndo
        {
            get { return undoActions.Count > 0; }
        }

		/// <summary>
		/// Gets a value indicating whether a "redo" can be done
		/// </summary>
        public bool CanRedo
        {
            get { return redoActions.Count > 0; }
        }

		/// <summary>
		/// Gets a collection of all currently selected items
		/// </summary>
        public ReadOnlyCollection<MenuItem> SelectedItems
        {
            get { return selectedItems.AsReadOnly(); }
        }

		/// <summary>
		/// Gets a copy of the selected items collection as an array
		/// </summary>
        public MenuItem[] SelectedItemsArray
        {
            get { return selectedItems.ToArray(); }
        }

		/// <summary>
		/// Gets the filename to which the project will be saved
		/// </summary>
		public string ProjectFileName
		{
			get { return projectFileName; }
		}

        /// <summary>
        /// Gets a value indicating whether the project is currently
        /// in preview mode
        /// </summary>
        public bool PreviewMode
        {
            get
            {
                if (projectHost == null)
                    return false;
                else
                    return projectHost.PreviewMode;
            }
        }

		/// <summary>
		/// Creates a new ActionManager instance
		/// </summary>
        public ActionManager(MainFrm mainForm)
        {
            this.mainForm = mainForm;

            projectHost = new MenuProjectHost(mainForm, this);
        }

        /// <summary>
        /// Creates a new MenuProject
        /// </summary>
        public void CreateNewProject()
        {
			if (!CloseProject())
				return;

			try
			{
                CreateTempDirectory();

				MenuProject menuProject = new MenuProject();
				menuProject.Dimensions = new Point(1280, 720);
				menuProject.Initialize(projectHost.ServiceCollection, MainFrm.TempContentDirectory);
				menuProject.ToggleDesignMode(true);

				Page page = (Page)menuProject.AddNewItemToProject(typeof(Page), menuProject);

				MenuProject = menuProject;
				dirty = true;
			}
			catch (ContentLoadException e)
			{
				mainForm.ShowMessage("An error occurred while attempting to load the project's content: " + e.Message + "\n\n"
										+ "Make sure you have the proper Content Folder set for this project.", System.Windows.Forms.MessageBoxIcon.Error);
			}            
        }

		/// <summary>
		/// Opens an existing MenuProject
		/// </summary>
		/// <param name="fileName"></param>
		public void OpenProject(string fileName)
		{
            if (string.IsNullOrEmpty(fileName))
			{
				Debug.Assert(false);
				return;
			}

			if (!CloseProject())
				return;

			try
			{
				using (FileStream stream = new FileStream(fileName, FileMode.Open))
				{
					XmlSerializer serializer = new XmlSerializer(typeof(MenuProject), CPXMenu.MenuProject.GetSerializationTypes());
					MenuProject project = (MenuProject)serializer.Deserialize(stream);
					project.Initialize(projectHost.ServiceCollection, "");
					project.ToggleDesignMode(true);
					MenuProject = project;

					serializer = null;
					stream.Close();

					projectFileName = fileName;
				}
			}
			catch (IOException e)
			{
				mainForm.ShowMessage("An error occurred while attempting to load the specified file: " + e.Message, System.Windows.Forms.MessageBoxIcon.Error);
			}
			catch (ContentLoadException e)
			{
				mainForm.ShowMessage("An error occurred while attempting to load the project's content: " + e.Message + "\n\n"
										+ "Make sure you have the proper Content Folder set for this project.", System.Windows.Forms.MessageBoxIcon.Error);
			}
			catch (Exception e)
			{
				mainForm.ShowMessage("An error occurred while attempting to load the specified file: " + e.Message, System.Windows.Forms.MessageBoxIcon.Error);
			}
		}

		/// <summary>
		/// Saves the project
		/// </summary>
        public void SaveProject()
        {
			if (projectHost == null || project == null)
				return;

            if (projectHost.PreviewMode)
                return;

			if (string.IsNullOrEmpty(projectFileName))
			{
				Debug.Assert(false);
				return;
			}

			try
			{
				using (FileStream stream = new FileStream(projectFileName, FileMode.Create))
				{
					XmlSerializer serializer = new XmlSerializer(typeof(MenuProject), CPXMenu.MenuProject.GetSerializationTypes());
					serializer.Serialize(stream, project);
					serializer = null;
					stream.Close();
				}

				dirty = false;
			}
			catch (IOException e)
			{
				mainForm.ShowMessage("An error occurred while attempting to save the project to disc: " + e.Message, System.Windows.Forms.MessageBoxIcon.Error);
			}
			catch (Exception e)
			{
				mainForm.ShowMessage("An error occurred while attempting to save the specified file: " + e.Message, System.Windows.Forms.MessageBoxIcon.Error);
			}            
        }

		/// <summary>
		/// Saves the project as a specified filename
		/// </summary>
		/// <param name="fileName">The filename to save the project as</param>
        public void SaveProjectAs(string fileName)
        {
            if (projectHost == null || project == null)
				return;

            if (projectHost.PreviewMode)
                return;

            if (string.IsNullOrEmpty(fileName))
			{
				Debug.Assert(false);
				return;
			}

            projectFileName = fileName;
            SaveProject();
        }

        /// <summary>
        /// Attempts to close an open project.
        /// </summary>
        /// <returns>True if the project could be closed, false otherwise</returns>
        public bool CloseProject()
        {
            if (project == null)
                return true;
            
            ExitPreviewMode();

            bool cancel = false;
            OnProjectClosing(ref cancel);

            if (cancel)
                return false;
            else
            {
                ClearSelectedItems();
                undoActions.Clear();
                redoActions.Clear();
                MenuProject = null;
                projectFileName = string.Empty;
				dirty = false;

                GC.Collect();

                DeleteTempDirectory();

                return true;
            }
        }

        /// <summary>
        /// Undoes the last action
        /// </summary>
        public void Undo()
        {
            if (projectHost.PreviewMode)
                return;

            if (undoActions.Count > 0)
            {
                // Get the last action performed
                IEditAction action = undoActions.Pop();

                // Undo it
                action.Undo();

                if (action is AddRemoveItemAction)
                {
                    AddRemoveItemAction addRemoveAction = action as AddRemoveItemAction;
                    if (addRemoveAction.Added)
                        foreach (MenuItem item in addRemoveAction.Items)
                            if (selectedItems.Contains(item))
                                DeselectItem(item);
                }

                // Put it on the stack of Redo items
                redoActions.Push(action);

                if (UndoOccurred != null)
                    UndoOccurred(this, action);
            }
        }

        /// <summary>
        /// Redoes the last undone action
        /// </summary>
        public void Redo()
        {
            if (projectHost.PreviewMode)
                return;

            if (redoActions.Count > 0)
            {
                // Get the last action undone
                IEditAction action = redoActions.Pop();

                // Redo it
                action.Do();

                if (action is AddRemoveItemAction)
                {
                    AddRemoveItemAction addRemoveAction = action as AddRemoveItemAction;
                    if (!addRemoveAction.Added)
                        foreach (MenuItem item in addRemoveAction.Items)
                            if (selectedItems.Contains(item))
                                DeselectItem(item);
                }

                // Put it back on the stack of Undo items
                undoActions.Push(action);

                if (RedoOccurred != null)
                    RedoOccurred(this, action);
            }
        }

		/// <summary>
		/// Sets a property value on an item, and adds the action to the history
		/// </summary>
		/// <param name="item">The item whose property should be set</param>
		/// <param name="propertyName">The name of the property that should be set</param>
		/// <param name="newValue">The new value of the property</param>
        public void SetPropertyValue(object item, string propertyName, object newValue)
        {
            object[] items = new object[1];
            items[0] = item;
            SetPropertyValue(items, propertyName, newValue);
        }

		/// <summary>
		/// Sets a property value on multiple items, and adds the action to the history
		/// </summary>
		/// <param name="item">The items whose properties should be set</param>
		/// <param name="propertyName">The name of the properties that should be set</param>
		/// <param name="newValue">The new value of the properties</param>
        public void SetPropertyValue(object[] items, string propertyName, object newValue)
        {            
            object[] oldValues = new object[items.Length];

            for (int i = 0; i < oldValues.Length; i++)
                oldValues[i] = GetPropertyOrFieldValue(items[i], propertyName);

            PropertyEditAction action = new PropertyEditAction(items, oldValues, newValue, propertyName);
            action.Do();
            PushAction(action);
        }

		/// <summary>
		/// Sets a property value on multiple items, and adds the action to the history
		/// </summary>
		/// <param name="item">The items whose properties should be set</param>
		/// <param name="propertyName">The name of the properties that should be set</param>
		/// <param name="newValue">The new values of the properties</param>
        public void SetPropertyValue(object[] items, string propertyName, object[] newValues)
        {
            object[] oldValues = new object[items.Length];

            for (int i = 0; i < oldValues.Length; i++)
                oldValues[i] = GetPropertyOrFieldValue(items[i], propertyName);

            PropertyEditAction action = new PropertyEditAction(items, oldValues, newValues, propertyName);
            action.Do();
            PushAction(action);
        }

		/// <summary>
		/// Used to indicate that a property value has changed for multiple items.
		/// Adds the action to the history.
		/// </summary>
		/// <param name="items">The items whose properties were modified</param>
		/// <param name="propertyName">The name of the property that was modified</param>
		/// <param name="oldValues">The previous values of the properties before modification</param>
        public void PropertyValueChanged(object[] items, string propertyName, object[] oldValues)
        {
            object[] newValues = new object[items.Length];
            for (int i = 0; i < newValues.Length; i++)
                newValues[i] = GetPropertyOrFieldValue(items[i], propertyName);
            PropertyEditAction action = new PropertyEditAction(items, oldValues, newValues, propertyName);
            PushAction(action);
        }

		/// <summary>
		/// Adds a new item instance to the project, as a child of the specified parent.
		/// Adds the action to the history.
		/// </summary>
		/// <param name="itemType">The Type of MenuItem to add</param>
		/// <param name="parent">The parent that the new item should be added to</param>
        public MenuItem AddNewItem(Type itemType, MenuItem parent)
        {
			MenuItem newItem = null;

			try
			{
				newItem = project.AddNewItemToProject(itemType, parent);
			}
			catch (ContentLoadException e)
			{
				mainForm.ShowMessage("An error occurred while attempting to load content for the item being added: " + e.Message + "\n\n"
										+ "Make sure you have the proper Content Folder set for this project.", System.Windows.Forms.MessageBoxIcon.Error);
			}
            
            MenuItem[] items = new MenuItem[1];
            MenuItem[] parents = new MenuItem[1];
            items[0] = newItem;
            parents[0] = parent;
            AddRemoveItemAction action = new AddRemoveItemAction(this, items, parents, true);
			PushAction(action);

			return newItem;
        }

		/// <summary>
		/// Adds an already-created item to the project, as a child of the specified parent.
		/// Adds the action to the history.
		/// </summary>
		/// <param name="item">The item to add and reinitialize</param>
		/// <param name="parent">The parent that the item should be added to</param>
        public void AddItem(MenuItem item, MenuItem parent)
        {
            MenuItem[] items = new MenuItem[1];
            MenuItem[] parents = new MenuItem[1];
            items[0] = item;
            parents[0] = parent;
            AddItems(items, parents);
        }

		/// <summary>
		/// Adds multiple already-created items to the project, as children of the specified parents.
		/// Adds the action to the history.
		/// </summary>
		/// <param name="item">The items to add and reinitialize</param>
		/// <param name="parent">The parents that the items should be added to</param>
        public void AddItems(MenuItem[] items, MenuItem[] parents)
        {
            AddRemoveItemAction action = new AddRemoveItemAction(this, items, parents, true);
            action.Do();
            PushAction(action);
        }

		/// <summary>
		/// Removes an item from the project, and adds an action to the history.
		/// </summary>
		/// <param name="item">The item to remove</param>
        public void RemoveItem(MenuItem item)
        {
            if (item == null)
            {
                Debug.Assert(false);
                return;
            }

            MenuItem[] items = new MenuItem[1];
            MenuItem[] parents = new MenuItem[1];
            items[0] = item;
            parents[0] = item.Parent;
            RemoveItems(items);
        }

		/// <summary>
		/// Removes multiple items from the project, and adds an action to the history.
		/// </summary>
		/// <param name="item">The items to remove</param>
        public void RemoveItems(MenuItem[] items)
        {
            if (items.Length == 0)
                return;

            MenuItem[] parents = new MenuItem[items.Length];
            for (int i = 0; i < items.Length; i++)
                parents[i] = items[i].Parent;

            AddRemoveItemAction action = new AddRemoveItemAction(this, items, parents, false);
            action.Do();
            PushAction(action);

            foreach (MenuItem item in items)
                if (selectedItems.Contains(item))
                    DeselectItem(item);            
        }

        /// <summary>
        /// Removes all currently selected items from the project, and adds an action to the history.
        /// </summary>
        public void RemoveSelectedItems()
        {
            RemoveItems(SelectedItemsArray);
        }

		/// <summary>
		/// Marks the beginning of a "compound action".  All subsequent actions
		/// performed after calling this and before calling EndCompoundAction() 
		/// will be recorded and added to the history as a single action to be 
		/// undone or redone.
		/// </summary>
        public void BeginCompoundAction()
        {
            Debug.Assert(!compoundingAction);

            compoundingAction = true;
            compoundActions.Clear();
        }

		/// <summary>
		/// Marks the end of a "compound action".  All actions performed
		/// previous to calling this and after calling BeginCompoundAction()
		/// will be recorded and added to the history as a single action.
		/// </summary>
        public void EndCompoundAction()
        {
            Debug.Assert(compoundingAction);
            compoundingAction = false;

            CompoundAction compoundAction = new CompoundAction(compoundActions.ToArray());
            PushAction(compoundAction);

            compoundActions.Clear();
        }


		/// <summary>
		/// Deselects all items.
		/// </summary>
        public void ClearSelectedItems()
        {
            selectedItems.Clear();
            OnSelectionChanged();
        }

		/// <summary>
		/// Selects an item
		/// </summary>
		/// <param name="item">The item to select</param>
		/// <param name="multiSelect">If true, doesn't deselect the previously selected items</param>
        public void SelectItem(MenuItem item, bool multiSelect)
        {
            if (!multiSelect)
                selectedItems.Clear();

            MenuItem[] items = new MenuItem[1];
            items[0] = item;
            SelectItems(items, true);
        }

		/// <summary>
		/// Selects multiple items
		/// </summary>
		/// <param name="item">The items to select</param>
		/// <param name="multiSelect">If true, doesn't deselect the previously selected items</param>
        public void SelectItems(MenuItem[] items, bool multiSelect)
        {
            if (!multiSelect)
                selectedItems.Clear();

            foreach (MenuItem item in items)
                if (!selectedItems.Contains(item))
                    selectedItems.Add(item);

            OnSelectionChanged();
        }

		/// <summary>
		/// Deselects an item
		/// </summary>
		/// <param name="item">The item to deselect</param>
        public void DeselectItem(MenuItem item)
        {
            if (!selectedItems.Contains(item))
            {
                Debug.Assert(false);
                return;
            }

            selectedItems.Remove(item);
            OnSelectionChanged();
        }

        /// <summary>
        /// Enters preview mode
        /// </summary>
        public void EnterPreviewMode()
        {
            if (projectHost == null || projectHost.MenuProject == null)
                return;

            if (projectHost.PreviewMode)
                return;

            if (EnteringPreviewMode != null)
                EnteringPreviewMode(this);

            ClearSelectedItems();

            projectHost.EnterPreviewMode();
        }

        /// <summary>
        /// Exits preview mode
        /// </summary>
        public void ExitPreviewMode()
        {
            if (projectHost == null || projectHost.MenuProject == null)
                return;

            if (!projectHost.PreviewMode)
                return;

            if (LeavingPreviewMode != null)
                LeavingPreviewMode(this);
           
            projectHost.ExitPreviewMode();
        }

        public void AddImportedContent(ImportedContentItem item, ImportedContentDirectory parentDirectory)
        {
            ImportedContentItem[] items = { item };
            ImportedContentDirectory[] parentDirectories = { parentDirectory };
            AddImportedContent(items, parentDirectories);
        }

        public void AddImportedContent(ImportedContentItem[] items, ImportedContentDirectory[] parentDirectories)
        {
            if (items == null || items.Length == 0 || items.Length != parentDirectories.Length)
            {
                Debug.Assert(false);
                return;
            }

            AddRemoveImportedContentAction action = new AddRemoveImportedContentAction(this, items, parentDirectories, true);
            action.Do();
            PushAction(action);
        }

        public void RemoveImportedContent(ImportedContentItem item, ImportedContentDirectory parentDirectory)
        {
            ImportedContentItem[] items = { item };
            ImportedContentDirectory[] parentDirectories = { parentDirectory };
            RemoveImportedContent(items, parentDirectories);
        }

        public void RemoveImportedContent(ImportedContentItem[] items, ImportedContentDirectory[] parentDirectories)
        {
            if (items == null || items.Length == 0 || items.Length != parentDirectories.Length)
            {
                Debug.Assert(false);
                return;
            }

            AddRemoveImportedContentAction action = new AddRemoveImportedContentAction(this, items, parentDirectories, false);
            action.Do();
            PushAction(action);
        }

        public void AddImportedContentDirectory(ImportedContentDirectory item, ImportedContentDirectory parentDirectory)
        {
            ImportedContentDirectory[] items = { item };
            ImportedContentDirectory[] parentDirectories = { parentDirectory };
            AddImportedContentDirectory(items, parentDirectories);
        }

        public void AddImportedContentDirectory(ImportedContentDirectory[] items, ImportedContentDirectory[] parentDirectories)
        {
            if (items == null || items.Length == 0)
            {
                Debug.Assert(false);
                return;
            }

            AddRemoveImportedContentDirectoryAction action = new AddRemoveImportedContentDirectoryAction(this, items, parentDirectories, true);
            action.Do();
            PushAction(action);
        }

        public void RemoveImportedContentDirectory(ImportedContentDirectory item, ImportedContentDirectory parentDirectory)
        {
            ImportedContentDirectory[] items = { item };
            ImportedContentDirectory[] parentDirectories = { parentDirectory };
            RemoveImportedContentDirectory(items, parentDirectories);
        }

        public void RemoveImportedContentDirectory(ImportedContentDirectory[] items, ImportedContentDirectory[] parentDirectories)
        {
            if (items == null || items.Length == 0)
            {
                Debug.Assert(false);
                return;
            }

            AddRemoveImportedContentDirectoryAction action = new AddRemoveImportedContentDirectoryAction(this, items, parentDirectories, false);
            action.Do();
            PushAction(action);
        }

		/// <summary>
		/// Called when the collection of selected items changes.
		/// Raises the SelectionChanged event.
		/// </summary>
        protected void OnSelectionChanged()
        {
            if (SelectionChanged != null)
                SelectionChanged(this);
        }        		

		/// <summary>
		/// Called when the project changes.  Raises the ProjectChanged event
		/// </summary>
		/// <param name="project">The project to set</param>
        protected void OnProjectChanged(MenuProject oldProject, MenuProject newProject)
        {
            if (ProjectChanged != null)
                ProjectChanged(this, oldProject, newProject);
        }

		/// <summary>
		/// Called when a project is closing
		/// </summary>
		/// <param name="cancel">If set to true, closing will be canceled</param>
		protected void OnProjectClosing(ref bool cancel)
		{
			if (ProjectClosing != null)
				ProjectClosing(this, project, ref cancel);
		}

		/// <summary>
		/// Pushes a new action onto the undo stack.
		/// </summary>
		/// <param name="action">The action to add to the stack</param>
		private void PushAction(IEditAction action)
		{
			if (compoundingAction)
				compoundActions.Add(action);
			else
			{
				undoActions.Push(action);
				redoActions.Clear();
				dirty = true;
			}

			if (ActionOccurred != null)
				ActionOccurred(this, action);
		}

        private void CreateTempDirectory()
        {
            DeleteTempDirectory();

            Directory.CreateDirectory(MainFrm.TempProjectDirectory);
            Directory.CreateDirectory(MainFrm.TempContentDirectory);
            Directory.CreateDirectory(MainFrm.TempImportContentDirectory);
            Directory.CreateDirectory(MainFrm.TempDefaultContentDirectory);

            string defaultContentDir = Path.Combine(Path.Combine(MainFrm.AppPath, "Content"), "CPXMenu Content");
            CopyDirectory(defaultContentDir, MainFrm.TempDefaultContentDirectory);
        }

        private void DeleteTempDirectory()
        {
            if (Directory.Exists(MainFrm.TempProjectDirectory))
                Directory.Delete(MainFrm.TempProjectDirectory, true);
        }

        private static void CopyDirectory(string source, string destination)
        {
            string[] files = Directory.GetFiles(source);
            foreach (string file in files)
            {
                string newFile = Path.Combine(destination, Path.GetFileName(file));
                File.Copy(file, newFile);
            }

            string[] directories = Directory.GetDirectories(source);
            foreach(string directory in directories)
            {
                string newDir = destination + directory.Substring(source.Length);
                Directory.CreateDirectory(newDir);
                CopyDirectory(directory, newDir);
            }
        }

		/// <summary>
		/// Gets the value of the property or field belonging to the specified
		/// object, with the specified name.
		/// </summary>
		/// <param name="owner">The object who the property or field belongs to</param>
		/// <param name="propertyName">The name of the property or field</param>
		/// <returns>The value of the property or field if found, null otherwise</returns>
        public static object GetPropertyOrFieldValue(object owner, string propertyName)
        {
            Type ownerType = owner.GetType();
            // Try property first, then public field
            PropertyInfo property = ownerType.GetProperty(propertyName);
            if (property != null)
                return property.GetValue(owner, null);
            else
            {
                FieldInfo field = ownerType.GetField(propertyName);
                if (field != null)
                    return field.GetValue(owner);
                else
                    Debug.Assert(false);
            }

            return null;
        }
                
		/// <summary>
		/// Raised when a user-initated action occurs that edits the project
		/// </summary>
        public event ActionDelegate ActionOccurred;

		/// <summary>
		/// Raised when the user initiazes a Undo
		/// </summary>
        public event ActionDelegate UndoOccurred;

		/// <summary>
		/// Raised when the user initiates a Redo
		/// </summary>
        public event ActionDelegate RedoOccurred;

		/// <summary>
		/// Raised when currently selected items changes
		/// </summary>
        public event ActionManagerDelegate SelectionChanged;

        /// <summary>
        /// Raised when the current project changes
        /// </summary>
        public event ProjectChangedDelegate ProjectChanged;

		/// <summary>
		/// Raised when a project is closing
		/// </summary>
		public event ProjectClosingDelegate ProjectClosing;

        /// <summary>
        /// Raised when entering preview mode
        /// </summary>
        public event ActionManagerDelegate EnteringPreviewMode;

        /// <summary>
        /// Raised when leaving preview mode
        /// </summary>
        public event ActionManagerDelegate LeavingPreviewMode;
    }

    public delegate void ActionDelegate(ActionManager manager, IEditAction action);
    public delegate void ActionManagerDelegate(ActionManager manager);
    public delegate void ProjectChangedDelegate(ActionManager manager, MenuProject oldProject, MenuProject newProject);
	public delegate void ProjectClosingDelegate(ActionManager manager, MenuProject project, ref bool cancel);
}